package com.yc.fedex.model.request.ship;

import com.yc.fedex.model.domain.*;
import com.yc.fedex.model.request.rate.RateAndTransitTimesRequest;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Data
public class CreateShipmentRequest {

    private String mergeLabelDocOption;
    private RequestedShipment requestedShipment;
    private String labelResponseOptions;
    private AccountNumber accountNumber;
    private String shipAction;
    private String processingOptionType;
    private String oneLabelAtATime;

    private CreateShipmentRequest(Steps steps) {
        this.requestedShipment = steps.requestedShipment;
        this.labelResponseOptions = steps.labelResponseOptions;
        this.accountNumber = steps.accountNumber;
    }

    public static AccountNumberStep request() {
        return new Steps();
    }

    public interface AccountNumberStep {
        RequestedShipmentStep accountNumberStep(String accountNumber);
    }

    public interface RequestedShipmentStep {
        LabelResponseOptionsStep requestedShipmentStep(RequestedShipment requestedShipment);

    }

    public interface LabelResponseOptionsStep {
        Build labelResponseOptionsStep(String labelResponseOptions);
    }

    public interface Build {
        CreateShipmentRequest build();
    }

    public static class Steps implements RequestedShipmentStep, LabelResponseOptionsStep, AccountNumberStep, Build {
        private RequestedShipment requestedShipment;
        private String labelResponseOptions;
        private AccountNumber accountNumber;

        @Override
        public RequestedShipmentStep accountNumberStep(String accountNumber) {
            this.accountNumber = new AccountNumber(accountNumber);
            return this;
        }

        @Override
        public LabelResponseOptionsStep requestedShipmentStep(RequestedShipment requestedShipment) {
            this.requestedShipment = requestedShipment;
            return this;
        }

        @Override
        public Build labelResponseOptionsStep(String labelResponseOptions) {
            this.labelResponseOptions = labelResponseOptions;
            return this;
        }

        @Override
        public CreateShipmentRequest build() {
            return new CreateShipmentRequest(this);
        }
    }

    public static class RequestedShipment extends com.yc.fedex.model.domain.RequestedShipment {

        private RequestedShipment(Steps steps) {
            setShipper(steps.shipper);
            setRecipients(steps.recipients);
            setRequestedPackageLineItems(steps.requestedPackageLineItems);
            setRateRequestType(steps.rateRequestType);
            setPickupType(steps.pickupType);
            setServiceType(steps.serviceType);
            setPackagingType(steps.packagingType);
            setTotalWeight(steps.totalWeight);
            setShippingChargesPayment(steps.shippingChargesPayment);
            setLabelSpecification(steps.labelSpecification);
            setCustomsClearanceDetail(steps.customsClearanceDetail);
            setSoldTo(steps.soldTo);
            setTotalPackageCount(steps.totalPackageCount);
        }

        public static ShipperStep request() {
            return new Steps();
        }

        public interface ShipperStep {
            RecipientStep shipperStep(Address address, Contact contact);
        }

        public interface RecipientStep {
            SoldToStep recipientStep(Address address, Contact contact);
        }

        public interface SoldToStep {
            PickupTypeStep soldToStep(String accountNumber, Address address, Contact contact);
        }

        public interface PickupTypeStep {
            ServiceTypeStep pickupTypeStep(String pickupType);
        }

        public interface ServiceTypeStep {
            PackagingTypeStep serviceTypeStep(String serviceType);
        }

        public interface PackagingTypeStep {
            TotalWeightStep packagingTypeStep(String packagingType);
        }

        public interface TotalWeightStep {
            TotalPackageCountStep totalWeightStep(Double totalWeight);
        }

        public interface TotalPackageCountStep {
            ShippingChargesPaymentStep totalPackageCountStep(Integer totalPackageCount);
        }

        public interface ShippingChargesPaymentStep {
            CustomsClearanceDetailStep shippingChargesPaymentStep(String paymentType, String accountNumber);
        }

        public interface CustomsClearanceDetailStep {
            LabelSpecificationStep customsClearanceDetailStep(CustomsClearanceDetail customsClearanceDetail);
        }

        public interface LabelSpecificationStep {
            RequestedPackageLineItemsStep labelSpecificationStep(String labelStockType, String imageType);
        }

        public interface RequestedPackageLineItemsStep {
            RateRequestTypeStep addRequestedPackageLineItem(WeightUnits weight);

            RateRequestTypeStep requestedPackageLineItemsStep(List<WeightUnits> weights);

            RateRequestTypeStep packageLineItemsStep(List<RequestedPackageLineItems> requestedPackageLineItems);
        }

        public interface RateRequestTypeStep {
            Build rateRequestTypeStep(List<String> rateRequestType);
        }

        public interface Build {
            RequestedShipment build();
        }


        public static class Steps implements ShipperStep, RecipientStep, SoldToStep, PickupTypeStep, ServiceTypeStep, PackagingTypeStep, TotalWeightStep, TotalPackageCountStep,ShippingChargesPaymentStep, CustomsClearanceDetailStep, LabelSpecificationStep, RequestedPackageLineItemsStep, RateRequestTypeStep, Build {

            private Trader shipper;

            private List<Trader> recipients;

            private Payment shippingChargesPayment;

            private List<RequestedPackageLineItems> requestedPackageLineItems;

            private List<String> rateRequestType;

            private String pickupType;

            private String serviceType;

            private String packagingType;

            private Double totalWeight;

            private LabelSpecification labelSpecification;

            private CustomsClearanceDetail customsClearanceDetail;

            private Trader soldTo;

            private Integer totalPackageCount;

            @Override
            public RequestedShipment build() {
                return new RequestedShipment(this);
            }

            @Override
            public RecipientStep shipperStep(Address address, Contact contact) {
                this.shipper = Trader.builder().address(address).contact(contact).build();
                return this;
            }

            @Override
            public SoldToStep recipientStep(Address address, Contact contact) {
                this.recipients = List.of(Trader.builder().address(address).contact(contact).build());
                return this;
            }

            @Override
            public PickupTypeStep soldToStep(String accountNumber, Address address, Contact contact) {
                this.soldTo = Trader.builder().address(address).contact(contact).accountNumber(new AccountNumber(accountNumber)).build();
                return this;
            }

            @Override
            public ServiceTypeStep pickupTypeStep(String pickupType) {
                this.pickupType = pickupType;
                return this;
            }

            @Override
            public PackagingTypeStep serviceTypeStep(String serviceType) {
                this.serviceType = serviceType;
                return this;
            }

            @Override
            public TotalWeightStep packagingTypeStep(String packagingType) {
                this.packagingType = packagingType;
                return this;
            }

            @Override
            public TotalPackageCountStep totalWeightStep(Double totalWeight) {
                this.totalWeight = totalWeight;
                return this;
            }

            @Override
            public ShippingChargesPaymentStep totalPackageCountStep(Integer totalPackageCount) {
                this.totalPackageCount = totalPackageCount;
                return this;
            }

            @Override
            public CustomsClearanceDetailStep shippingChargesPaymentStep(String paymentType, String accountNumber) {
                this.shippingChargesPayment = Payment.builder().payor(new Payor(Trader.builder().accountNumber(new AccountNumber(accountNumber)).build())).paymentType(paymentType).build();
                return this;
            }

            @Override
            public LabelSpecificationStep customsClearanceDetailStep(CustomsClearanceDetail customsClearanceDetail) {
                this.customsClearanceDetail = customsClearanceDetail;
                return this;
            }

            @Override
            public RequestedPackageLineItemsStep labelSpecificationStep(String labelStockType, String imageType) {
                this.labelSpecification = LabelSpecification.builder().labelStockType(labelStockType).imageType(imageType).build();
                return this;
            }

            @Override
            public RateRequestTypeStep addRequestedPackageLineItem(WeightUnits weight) {
                if (this.requestedPackageLineItems == null) {
                    this.requestedPackageLineItems = new ArrayList<>();
                }
                requestedPackageLineItems.add(RequestedPackageLineItems.builder().weight(weight).build());
                return this;
            }

            @Override
            public RateRequestTypeStep requestedPackageLineItemsStep(List<WeightUnits> weights) {
                this.requestedPackageLineItems = weights.stream().map(item -> RequestedPackageLineItems.builder().weight(item).build()).collect(Collectors.toList());
                return this;
            }

            @Override
            public RateRequestTypeStep packageLineItemsStep(List<RequestedPackageLineItems> requestedPackageLineItems) {
                this.requestedPackageLineItems = requestedPackageLineItems;
                return this;
            }

            @Override
            public Build rateRequestTypeStep(List<String> rateRequestType) {
                this.rateRequestType = rateRequestType;
                return this;
            }
        }

        public static class CustomsClearanceDetail extends com.yc.fedex.model.domain.CustomsClearanceDetail {

            private CustomsClearanceDetail(Steps steps) {
                setTotalCustomsValue(steps.totalCustomsValue);
                setDutiesPayment(steps.dutiesPayment);
                setCommodities(steps.commodities);
            }

            public static DutiesPaymentStep request() {
                return new Steps();
            }

            public interface DutiesPaymentStep {
                CommoditiesStep dutiesPaymentStep(String paymentType, String accountNumber);
            }

            public interface CommoditiesStep {
                TotalCustomsValueStep commoditiesStep(List<Commodities> commodities);
            }

            public interface TotalCustomsValueStep {
                Build totalCustomsValueStep(AmountUnit totalCustomsValue);
            }

            public interface Build {
                CustomsClearanceDetail build();
            }

            public static class Steps implements DutiesPaymentStep, CommoditiesStep, TotalCustomsValueStep, Build {

                private AmountUnit totalCustomsValue;

                private Payment dutiesPayment;

                private List<Commodities> commodities;

                @Override
                public CustomsClearanceDetail build() {
                    return new CustomsClearanceDetail(this);
                }

                @Override
                public Build totalCustomsValueStep(AmountUnit totalCustomsValue) {
                    this.totalCustomsValue = totalCustomsValue;
                    return this;
                }

                @Override
                public CommoditiesStep dutiesPaymentStep(String paymentType, String accountNumber) {
                    this.dutiesPayment = Payment.builder().payor(new Payor(Trader.builder().accountNumber(new AccountNumber(accountNumber)).build())).paymentType(paymentType).build();
                    return this;
                }

                @Override
                public TotalCustomsValueStep commoditiesStep(List<Commodities> commodities) {
                    this.commodities = commodities;
                    return this;
                }
            }


        }
    }

}
